package com.windgo.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.windgo.bean.*;
import com.windgo.bean.common.BasePageInfo;
import com.windgo.bean.common.CommonData;
import com.windgo.bean.vo.MarketCouponListVO;
import com.windgo.bean.vo.WxCouponMyListVO;
import com.windgo.bean.wxVo.SelectListResponseVO;
import com.windgo.mapper.MarketCartMapper;
import com.windgo.mapper.MarketCouponMapper;
import com.windgo.mapper.MarketCouponUserMapper;
import com.windgo.util.StringUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author:ZhouXin
 * @Date:2022/10/12 23:23
 * @Description:
 **/
@Service
public class CouponServiceImpl implements CouponService {
    // 优惠券默认使用状态码：0表示为未使用状态
    private static final Short COUPON_DEFAULT_STATUS = 0;
    @Autowired
    MarketCouponMapper marketCouponMapper;

    @Autowired
    MarketCouponUserMapper marketCouponUserMapper;

    @Autowired
    MarketCartMapper marketCartMapper;

    /**
     * 显示所有的优惠券
     *
     * @param info
     * @param name
     * @param type
     * @param status
     * @return
     */
    @Override
    public CommonData<MarketCoupon> list(BasePageInfo info, String name, Short type, Short status) {
        PageHelper.startPage(info.getPage(), info.getLimit());

        MarketCouponExample example = new MarketCouponExample();
        example.setOrderByClause(info.getSort() + " " + info.getOrder());
        MarketCouponExample.Criteria criteria = example.createCriteria();

        //判断
        if (!StringUtil.isEmpty(name)) {
            criteria.andNameLike("%" + name + "%");
        }
        if (type != null) {
            criteria.andTypeEqualTo(type);
        }
        if (status != null) {
            criteria.andStatusEqualTo(status);
        }
        List<MarketCoupon> list = marketCouponMapper.selectByExample(example);

        PageInfo<MarketCoupon> pageInfo = new PageInfo<>(list);

        return CommonData.data(pageInfo);
    }

    /**
     * 添加优惠券
     *
     * @param marketCoupon
     * @return
     */
    @Override
    public MarketCoupon create(MarketCoupon marketCoupon) {

        Date date = new Date();
        marketCoupon.setAddTime(date);
        marketCoupon.setUpdateTime(date);
        marketCouponMapper.insertSelective(marketCoupon);
        Integer id = marketCoupon.getId();
        //根据Id
        MarketCoupon data = marketCouponMapper.selectByPrimaryKey(id);
        return data;
    }

    /**
     * 修改优惠券
     *
     * @param marketCoupon
     * @return
     */
    @Override
    public MarketCoupon update(MarketCoupon marketCoupon) {
        Integer id = marketCoupon.getId();
        Date date = new Date();
        marketCoupon.setUpdateTime(date);
        marketCouponMapper.updateByPrimaryKeySelective(marketCoupon);
        MarketCoupon data = marketCouponMapper.selectByPrimaryKey(id);
        return data;
    }

    /**
     * 删除优惠券
     *
     * @param marketCoupon
     */
    @Override
    public void delete(MarketCoupon marketCoupon) {
        Integer id = marketCoupon.getId();
        marketCouponMapper.deleteByPrimaryKey(id);
    }

    /**
     * 优惠券详情根据id读取信息
     *
     * @param id
     * @return
     */
    @Override
    public MarketCoupon read(Integer id) {
        MarketCoupon data = marketCouponMapper.selectByPrimaryKey(id);
        return data;
    }

    /**
     * 优惠券详情中和用户绑定的信息
     *
     * @param info
     * @param couponId
     * @return
     */
    @Override
    public CommonData<MarketCouponUser> listUser(BasePageInfo info, Integer couponId) {
        PageHelper.startPage(info.getPage(), info.getLimit());

        MarketCouponUserExample example = new MarketCouponUserExample();

        //排序
        example.setOrderByClause(info.getSort() + " " + info.getOrder());

        MarketCouponUserExample.Criteria criteria = example.createCriteria();

        criteria.andCouponIdEqualTo(couponId);

        List<MarketCouponUser> list = marketCouponUserMapper.selectByExample(example);

        PageInfo<MarketCouponUser> pageInfo = new PageInfo<>(list);

        return CommonData.data(pageInfo);
    }

    /**
     * 给wx小程序分页填充优惠券列表
     *
     * @param info
     * @return com.windgo.bean.common.CommonData<com.windgo.bean.MarketCoupon>
     * @author mabai
     * @since 2022/10/14 15:51
     */
    @Override
    public CommonData<MarketCouponListVO> listCoupon(BasePageInfo info) {
        // 开启分页
        PageHelper.startPage(info.getPage(), info.getLimit());
        MarketCouponExample example = new MarketCouponExample();
        MarketCouponExample.Criteria criteria = example.createCriteria();
        // 判断是否逻辑删除，如果逻辑删除不显示
        criteria.andDeletedEqualTo(false);
        // 获取查询结果封装的po对象
        List<MarketCoupon> couponList = marketCouponMapper.selectByExample(example);
        // 创建一个装vo对象的容器
        ArrayList<MarketCouponListVO> marketCouponListVOS = new ArrayList<>();
        // 遍历装有查询结果的po对象集合，将po对象的数据逐一封装到vo对象的容器中
        for (MarketCoupon marketCoupon : couponList) {
            Short days = marketCoupon.getDays();
            String desc = marketCoupon.getDesc();
            BigDecimal discount = marketCoupon.getDiscount();
            Integer id = marketCoupon.getId();
            BigDecimal min = marketCoupon.getMin();
            String name = marketCoupon.getName();
            String tag = marketCoupon.getTag();
            MarketCouponListVO marketCouponListVO = new MarketCouponListVO(days, desc, discount, id, min, name, tag);
            marketCouponListVOS.add(marketCouponListVO);
        }
        // 遍历结束后vo对象容器已经装有传给客户端的数据，并把vo对象容器进行分页
        PageInfo<MarketCouponListVO> pageInfo = new PageInfo<>(marketCouponListVOS);
        return CommonData.data(pageInfo);
    }

    /**
     * @param info
     * @param status
     * @return com.windgo.bean.common.CommonData<com.windgo.bean.vo.MarketCouponListVO>
     * @author mabai
     * @since 2022/10/14 20:23
     */
    @Override
    public CommonData<WxCouponMyListVO> listCouponUser(BasePageInfo info, Integer status) {
        // 获取当前用户id
        Integer userId = getCurrentUserId();
        // TODO:这里userId暂时写死
        // Integer userId = 15;
        // 根据status查询
        // 开启分页
        PageHelper.startPage(info.getPage(), info.getLimit());
        List<WxCouponMyListVO> list = marketCouponMapper.selectByUserIdAndStatus(userId, status);
        // 判断优惠券的状态码
        if (status == 0) {
            for (WxCouponMyListVO marketCouponListVO : list) {
                marketCouponListVO.setAvailable(true);
            }
        } else {
            for (WxCouponMyListVO marketCouponListVO : list) {
                marketCouponListVO.setAvailable(false);
            }
        }
        PageInfo<WxCouponMyListVO> pageInfo = new PageInfo<>(list);
        return CommonData.data(pageInfo);
    }

    /**
     * 用户领取优惠券的方法
     *
     * @param couponId
     * @return int
     * @author mabai
     * @since 2022/10/15 15:27
     */
    @Transactional
    @Override
    public int userReceiveCoupon(Integer couponId) {
        // 定义返回值即领取情况状态码
        int status;
        // 首先根据优惠券id查询优惠券表定位用户领取的是哪种优惠券
        MarketCoupon userReceivedCoupon = marketCouponMapper.selectByPrimaryKey(couponId);
        // 判断该优惠券类型是否为无限量供应类型
        Integer total = userReceivedCoupon.getTotal();
        // 等于0则为无限量供应类型，增加小于0的条件是为了覆盖所有数字范围，所以小于等于均表示为无限量供应类型优惠券
        if (total <= 0) {
            // 首先判断该用户的领券数量是否有限制
            Short userCouponCountLimit = userReceivedCoupon.getLimit(); // 用户领券限制数量，如果是0，则不限制领取数量；默认是1，限领一张
            if (userCouponCountLimit >= 1) {
                // userCouponCountLimit大于等于1以上，那么则不为0说明有领取数量限制
                // 查询该用户在优惠券用户表中领取该优惠券的数量
                int userCouponReceiveCount =
                        marketCouponUserMapper.selectUserCouponReceiveCountByUserIdAndCouponId(getCurrentUserId(), couponId);
                // 判断用户领券数量是否大于等于领取数量限制
                if (userCouponReceiveCount == userCouponCountLimit || userCouponReceiveCount > userCouponCountLimit) {
                    // TODO：如果大于领取数量限制往表中删除多余的优惠券，如出现该情况再实现该功能
                    // if (userCouponReceiveCount > userCouponCountLimit){
                    //     marketCouponUserMapper.deleteCouponByUserIdAndCouponId(getCurrentUserId(), couponId);
                    // }
                    status = 1;
                    return status;
                }
                // 执行到这里说明用户领券数量小于领取数量限制，允许领取
            }
            // 获取封装数据后的po对象
            MarketCouponUser marketCouponUser = getMarketCouponUser(couponId, userReceivedCoupon);
            // 获取无限量的优惠券，直接在优惠券用户表中插入一条记录即可
            int rowsByInsert = marketCouponUserMapper.insertSelective(marketCouponUser);
            if (rowsByInsert == 1) {
                status = 0;
                return status;
            }
            // 如果插入失败则领取失败返回系统异常
            status = 3;
            return status;
        }
        // 那么执行到这里说明是有限量供应优惠券，那么需要判断当前优惠券数量是否等于1（设定1为优惠券停止领取条件）
        if (total == 1) {
            // 数量等于1表示优惠券已经领取完,响应740
            status = 2;
            return status;
        }
        // 执行到这里说明total大于1，则进行领取操作，领取前需要先判断该用户的领券数量是否有限制
        Short userCouponCountLimit = userReceivedCoupon.getLimit(); // 用户领券限制数量，如果是0，则不限制领取数量；默认是1，限领一张
        if (userCouponCountLimit >= 1) {
            // userCouponCountLimit大于等于1以上，那么则不为0说明有领取数量限制
            // 查询该用户在优惠券用户表中领取该优惠券的数量
            int userCouponReceiveCount =
                    marketCouponUserMapper.selectUserCouponReceiveCountByUserIdAndCouponId(getCurrentUserId(), couponId);
            // 判断用户领券数量是否大于等于领取数量限制
            if (userCouponReceiveCount == userCouponCountLimit || userCouponReceiveCount > userCouponCountLimit) {
                // TODO：如果大于领取数量限制往表中删除多余的优惠券，如出现该情况再实现该功能
                // if (userCouponReceiveCount > userCouponCountLimit){
                //     marketCouponUserMapper.deleteCouponByUserIdAndCouponId(getCurrentUserId(), couponId);
                // }
                status = 1;
                return status;
            }
            // 执行到这里说明用户领券数量小于领取数量限制，允许领取
        }
        // 获取封装数据后的po对象
        MarketCouponUser marketCouponUser = getMarketCouponUser(couponId, userReceivedCoupon);
        // 将封装数据后的po对象传入sql语句中进行插入操作,并且减少优惠券表中的该优惠券数量
        boolean isReceiveSuccess = userGetFiniteCoupon(couponId, total, marketCouponUser);
        if (isReceiveSuccess) {
            status = 0;
            return status;
        }
        status = 3;
        return status;
    }

    /**
     * 处理用户兑换优惠券的方法
     *
     * @param code
     * @return int
     * @author mabai
     * @since 2022/10/16 11:43
     */
    @Transactional
    @Override
    public int userExchangeCoupon(String code) {
        // 兑换状态码，默认兑换失败
        int status = -1;
        // 首先根据code优惠券兑换码查询优惠券主表中的记录
        MarketCoupon userExchangeTypeCoupon = marketCouponMapper.selectExchangeTypeCouponByCode(code);
        if (userExchangeTypeCoupon == null) {
            status = 3;
            return status;
        }
        // 执行到这里说明不为null，则优惠券表中有该优惠券，那么需要判断该优惠券的total数量是否小于等于0，意思为该优惠券是否无限量兑换
        Integer total = userExchangeTypeCoupon.getTotal();
        if (total <= 0) {
            // 首先判断该用户的兑换券数量是否有限制
            Short userCouponCountLimit = userExchangeTypeCoupon.getLimit(); // 用户兑换券限制数量，如果是0，则不限制领取数量；默认是1，限领一张
            if (userCouponCountLimit >= 1) {
                // userCouponCountLimit大于等于1以上，那么则不为0说明有兑换数量限制
                // 查询该用户在优惠券用户表中兑换该优惠券的数量
                int userCouponReceiveCount =
                        marketCouponUserMapper.selectUserCouponReceiveCountByUserIdAndCouponId(getCurrentUserId(),
                                userExchangeTypeCoupon.getId());
                // 判断用户换券数量是否大于等于兑换数量限制
                if (userCouponReceiveCount == userCouponCountLimit || userCouponReceiveCount > userCouponCountLimit) {
                    // TODO：如果大于领取数量限制往表中删除多余的优惠券，如出现该情况再实现该功能
                    // if (userCouponReceiveCount > userCouponCountLimit){
                    //     marketCouponUserMapper.deleteCouponByUserIdAndCouponId(getCurrentUserId(), couponId);
                    // }
                    status = 2;
                    return status;
                }
                // 执行到这里说明用户领券数量小于领取数量限制，允许领取
            }
            // 执行到这里说明该优惠券兑换数量没有限制，进行兑换
            // 获取封装数据后的po对象
            MarketCouponUser marketCouponUser = getMarketCouponUser(userExchangeTypeCoupon.getId(), userExchangeTypeCoupon);
            // 获取无限量的优惠券，直接在优惠券用户表中插入一条记录即可
            int rowsByInsert = marketCouponUserMapper.insertSelective(marketCouponUser);
            if (rowsByInsert == 1) {
                // 兑换成功
                status = 0;
                return status;
            }
            // 如果插入失败则领取失败返回系统异常
            return status;
        }
        // 执行到这里说明该优惠券兑换数量大于等于1以上，属于有限量供应，需要判断该优惠券的total数量是否等于1
        // （优惠券兑换完毕的条件是total数量为1）
        if (total == 1) {
            // 数量等于1表示优惠券已经领取完,响应742
            status = 1;
            return status;
        }
        // 执行到这里说明还能兑换，那么判断用户兑换数量是否有限制
        Short userCouponCountLimit = userExchangeTypeCoupon.getLimit(); // 用户领券限制数量，如果是0，则不限制领取数量；默认是1，限领一张
        if (userCouponCountLimit >= 1) {
            // userCouponCountLimit大于等于1以上，那么则不为0说明有兑换数量限制
            // 查询该用户在优惠券用户表中兑换该优惠券的数量
            int userCouponReceiveCount =
                    marketCouponUserMapper.selectUserCouponReceiveCountByUserIdAndCouponId(getCurrentUserId(),
                            userExchangeTypeCoupon.getId());
            // 判断用户兑换数量是否大于等于兑换数量限制
            if (userCouponReceiveCount == userCouponCountLimit || userCouponReceiveCount > userCouponCountLimit) {
                // TODO：如果大于兑换数量限制往表中删除多余的优惠券，如出现该情况再实现该功能
                // if (userCouponReceiveCount > userCouponCountLimit){
                //     marketCouponUserMapper.deleteCouponByUserIdAndCouponId(getCurrentUserId(), couponId);
                // }
                status = 2;
                return status;
            }
            // 执行到这里说明用户领券数量小于领取数量限制，允许领取
        }
        // 执行到这里说明userCouponCountLimit小于1以下，那么范围是非正整数，则表示没有兑换数量限制
        // 有获取数量限制的和没有数量限制的优惠券领取操作统一在下面进行
        // 获取封装数据后的po对象
        MarketCouponUser marketCouponUser = getMarketCouponUser(userExchangeTypeCoupon.getId(), userExchangeTypeCoupon);
        // 将封装数据后的po对象传入sql语句中进行插入操作,并且减少优惠券表中的该优惠券数量
        boolean isReceiveSuccess = userGetFiniteCoupon(userExchangeTypeCoupon.getId(), total, marketCouponUser);
        if (isReceiveSuccess) {
            status = 0;
            return status;
        }
        return status;
    }

    /**
     * 处理用户下单时显示可用优惠券列表的方法
     *
     * @param cartId         购物车表id
     * @param grouponRulesId 团购规则表id
     * @return java.util.List<com.windgo.bean.wxVo.SelectListResponseVO>
     * @author mabai
     * @since 2022/10/16 17:37
     */
    @Override
    public CommonData<SelectListResponseVO> selectAvailableCouponList(Integer cartId, Integer grouponRulesId) {
        /*
        一、根据cartId判断用户选中了多件商品还是单件商品，其目的都是为了获取下单时的价格总和
        二、获取价格总和是为了判断价格总和是否高于用户的优惠券门槛，高于用户优惠券门槛则将用户的优惠券的available属性设为true
        三、最后响应用户的未使用的优惠券列表，但前端页面中只会显示available为true的优惠券
        */
        // 获取当前用户id
        Integer currentUserId = getCurrentUserId();
        // 定义一个价格总和的变量
        BigDecimal priceSum = new BigDecimal(0);
        // 判断cartId是否为0
        if (cartId == 0) {
            // 如果cartId等于0则根据checked=1查询购物车商品表,checked=1表示购物车商品表中被选购下单的商品
            List<MarketCart> cartList = marketCartMapper.selectCartByChecked(1);
            for (MarketCart cart : cartList) {
                // 获取下单页面价格总和
                priceSum = getPriceSum(priceSum, cart);
            }
        } else {
            // cartId不等于0则根据cartId查询购物车商品表
            MarketCart cart = marketCartMapper.selectByPrimaryKey(cartId);
            // 获取下单页面价格总和
            priceSum = getPriceSum(priceSum, cart);
        }
        // 获取当前用户所有未使用的优惠券
        List<SelectListResponseVO> list = marketCouponUserMapper.selectUserUnusedCouponByUserId(currentUserId);
        // 遍历用户未使用的优惠券list
        for (SelectListResponseVO selectListResponseVO : list) {
            // 优惠券使用门槛
            int couponThreshold = selectListResponseVO.getMin();
            BigDecimal couponThresholdBigDecimal = new BigDecimal(couponThreshold);
            // 判断商品价格总和是否大于等于优惠券使用门槛
            int compare = priceSum.compareTo(couponThresholdBigDecimal);
            // compare等于1 表示priceSum大于couponThresholdBigDecimal
            // compare等于0 表示priceSum等于couponThresholdBigDecimal
            // compare等于-1 表示priceSum小于couponThresholdBigDecimal
            // 如果compare大于等于0以上则表示当前优惠券可用，则把available设为true
            if (compare >= 0) {
                selectListResponseVO.setAvailable(true);
            }
        }
        // list赋值完成后，给data赋值
        CommonData<SelectListResponseVO> data = new CommonData<>();
        data.setPages(1);
        data.setPage(1);
        data.setLimit(list.size());
        data.setTotal(list.size());
        data.setList(list);
        return data;
    }

    /**
     * 获取商品价格总和
     *
     * @param priceSum
     * @param cart
     * @return java.math.BigDecimal
     * @author mabai
     * @since 2022/10/16 21:28
     */
    private BigDecimal getPriceSum(BigDecimal priceSum, MarketCart cart) {
        // 商品单价
        BigDecimal price = cart.getPrice();
        // 下单商品数量
        int productNum = cart.getNumber();
        // 将Short类型productNum转换为BigDecimal类型
        BigDecimal number = new BigDecimal(productNum);
        // 计算单条记录商品价格
        BigDecimal singleProductPriceSum = price.multiply(number);
        // 累加商品价格和
        priceSum = priceSum.add(singleProductPriceSum);
        return priceSum;
    }

    /**
     * 用户获取有限数量的优惠券
     *
     * @param couponId
     * @param total
     * @param marketCouponUser
     * @return boolean
     * @author mabai
     * @since 2022/10/15 22:21
     */
    private boolean userGetFiniteCoupon(Integer couponId, Integer total, MarketCouponUser marketCouponUser) {
        int rowsByInsert = marketCouponUserMapper.insertSelective(marketCouponUser);
        if (rowsByInsert != 1) {
            return false;
        }
        // 成功领取一张优惠券后，优惠券主表中的该优惠券数量需要-1
        MarketCoupon marketCoupon = new MarketCoupon();
        marketCoupon.setId(couponId);
        int afterDecreasedTotalCount = total - 1;
        marketCoupon.setTotal(afterDecreasedTotalCount);
        int rowsByUpdate = marketCouponMapper.updateByPrimaryKeySelective(marketCoupon);
        return rowsByUpdate == 1;
    }

    /**
     * 获取赋值完成后的MarketCouponUser PO对象
     *
     * @param couponId
     * @param userReceivedCoupon
     * @return com.windgo.bean.MarketCouponUser
     * @author mabai
     * @since 2022/10/15 22:05
     */
    private MarketCouponUser getMarketCouponUser(Integer couponId, MarketCoupon userReceivedCoupon) {
        MarketCouponUser marketCouponUser = new MarketCouponUser();
        marketCouponUser.setUserId(getCurrentUserId());
        marketCouponUser.setCouponId(couponId);
        marketCouponUser.setStatus(userReceivedCoupon.getStatus());
        // 设置优惠券的有效时间前需要判断优惠券的有效日期的类型
        Short couponValidTimeType = userReceivedCoupon.getTimeType();
        // 用户优惠券有效时间类型：分为以下两种
        // ①如果值为0，则基于领取时间的有效天数days，
        // 优惠券相对有效日期时间：即设置优惠券有效天数，根据用户当前领取日期时间设置优惠券相对有效日期时间，
        // 每个用户的优惠券生效失效时间根据用户领取时间而设定
        // ②如果值为1，则start_time和end_time是优惠券有效期，
        // 优惠券绝对有效日期时间：即按照优惠券表中的生效时间和失效时间确定设定所有用户相同的优惠券生效失效日期时间
        // 在switch语句中设置优惠券的有效时间
        switch (couponValidTimeType) {
            // 优惠券有效日期时间根据用户领取时间设置
            case 0:
                // 获取当前时间设置为优惠券生效时间
                Date startTime = getCurrentTime();
                marketCouponUser.setStartTime(startTime);
                // 获取优惠券到期时间设置为优惠券失效时间
                Date endTime = getAddDate(startTime, userReceivedCoupon.getDays());
                marketCouponUser.setEndTime(endTime);
                break;
            // 优惠券有效日期时间按照优惠券表中的有效时间设置
            case 1:
                // 获取优惠券表中的统一生效失效时间作为用户领取的优惠券有效时间
                marketCouponUser.setStartTime(userReceivedCoupon.getStartTime());
                marketCouponUser.setEndTime(userReceivedCoupon.getEndTime());
                break;
        }
        marketCouponUser.setAddTime(getCurrentTime());
        marketCouponUser.setUpdateTime(getCurrentTime());
        marketCouponUser.setDeleted(false);
        return marketCouponUser;
    }

    /**
     * 获取当前时间
     *
     * @return void
     * @author mabai
     * @since 2022/10/15 17:06
     */
    private Date getCurrentTime() {
        return new Timestamp(System.currentTimeMillis());
    }

    /**
     * 获取当前用户的用户id
     *
     * @return java.lang.Integer
     * @author mabai
     * @since 2022/10/15 16:19
     */
    private Integer getCurrentUserId() {
        Subject subject = SecurityUtils.getSubject();
        MarketUser primaryPrincipal = (MarketUser) subject.getPrincipals().getPrimaryPrincipal();
        // // 获取当前用户id
        return primaryPrincipal.getId();
    }

    /**
     * 用于计算起始时间增加天数后得到的到期时间
     *
     * @param validDays 有效天数
     * @param startTime 起始时间
     * @return java.sql.Timestamp
     * @author mabai
     * @since 2022/10/15 16:11
     */
    private Date getAddDate(Date startTime, Short validDays) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        calendar.add(Calendar.DATE, validDays);
        return new Timestamp(calendar.getTimeInMillis());
    }
}
